home *** CD-ROM | disk | FTP | other *** search
/ The World's Largest Collection of Windows Software / The World's Largest Collection of Windows Software - Disc 1.iso / connect / _j2 / wvnsc926 / wvattach.c < prev    next >
C/C++ Source or Header  |  1994-09-21  |  30KB  |  1,005 lines

  1. /********************************************************************
  2.  *                                                                  *
  3.  *  MODULE    :  WVATTACH.C                                         *
  4.  *                                                                  *
  5.  *  PURPOSE   : This file contains functions for attachments to     *
  6.  *              mail or news posting                                *
  7.  *                                                                  *
  8.  *  ENTRY POINTS: Attach ()                                         *
  9.  *                                                                  *
  10.  * Author: John S. Cooper (jcooper@netcom.com)                      *
  11.  *   Date: Nov 28, 1993                                             *
  12.  ********************************************************************/
  13. /* 
  14.  * $Id: wvattach.c 1.9 1994/09/16 00:57:09 jcooper Exp $
  15.  * $Log: wvattach.c $
  16.  * Revision 1.9  1994/09/16  00:57:09  jcooper
  17.  * zero-padding in subject template, cleanup for 92.6
  18.  *
  19.  * Revision 1.8  1994/08/24  17:59:26  jcooper
  20.  * misc encoding/decoding changes
  21.  *
  22.  * Revision 1.7  1994/08/11  00:09:17  jcooper
  23.  * Enhancements to Mime and article encoding/encoding
  24.  *
  25.  * Revision 1.6  1994/06/09  18:51:30  rushing
  26.  * problem with symbol 'header' on AXP/NT
  27.  *
  28.  * Revision 1.5  1994/05/23  18:37:00  jcooper
  29.  * new attach code, session [dis]connect
  30.  *
  31.  * Revision 1.3  1994/02/24  21:27:10  jcoop
  32.  * jcoop changes
  33.  *
  34.  * Revision 1.2  1994/01/22  01:30:03  jcoop
  35.  * 90.2 changes
  36.  *
  37.  * Revision 1.1  1994/01/16  12:10:08  jcoop
  38.  * Initial revision
  39.  *
  40.  */ 
  41. #include <windows.h>
  42. #include <windowsx.h>
  43. #include "wvglob.h"
  44. #include "winvn.h"
  45. #pragma hdrstop
  46. #include <string.h>
  47. #include <ctype.h>        /* for isspace, isalnum, etc */
  48. #include <stdlib.h>        /* for itoa */
  49.  
  50. extern void     UpdateBlockStatus ();    // in wvcoding.c
  51. /*
  52.  * Forward Declarations
  53.  */
  54. void    AppendPlainFileToPost (char *fileName);
  55. BOOL    SplitCurrentHeader (TypTextBlock *header, TypTextBlock *body, TypTextBlock *tail, char *origSubject);
  56. BOOL    AddMIMEVersion (TypTextBlock *header);
  57. BOOL    InitiateAttach (int useWnd, int DocType);
  58. void    EndAttach ();
  59. void    PostTextBlock (TypTextBlock *block);
  60. unsigned long PostPartialTextBlock (TypTextBlock *block, unsigned long start, unsigned long maxBytes);
  61. int     PostOneLine (char *str, unsigned long *byteCount, unsigned long maxBytes);
  62. void    GenerateSubject (TypTextBlock *header, char *origSubject, unsigned int part, unsigned int numParts);
  63. HWND    CreateGlobalPostingWnd (HWND hWnd);
  64. void     FlushCommSpool ();
  65.  
  66. /*
  67.  * Globals 
  68.  */
  69. // NUM_ENCODING_TYPES should be set in wvglob.h to the # items here
  70. char *EncodingTypes[] = { "Base-64", "UU", "XX", "Custom", "None" };
  71.  
  72. // NUM_CONTENT_TYPES should be set in wvglob.h to the # items here
  73. char *ContentTypes[] =  
  74.     {    "Text/Plain",
  75.         "Text/Richtext",
  76.         "Video/MPEG",
  77.         "Video/AVI",
  78.         "Image/JPEG",
  79.         "Image/GIF",
  80.         "Audio/Basic",
  81.         "Application/Zip",
  82.         "Application/PostScript",
  83.         "Other" };
  84.  
  85. #define CURRENT_WND     1
  86. #define NEW_WND        2
  87. #define EDIT_SIZE_INC    512
  88.  
  89. #define ATTACH_NONE              0
  90. #define ATTACH_START             1     // state constants for ProcessAttachment
  91. #define ATTACH_FIRST_IN_THIS_WND 2    
  92. #define ATTACH_FIRST_IN_NEXT_WND 3 
  93. #define ATTACH_WAIT              4
  94. #define ATTACH_MAIN              5
  95. #define ATTACH_DONE              6
  96.  
  97. HWND hThisEditWnd;
  98. HWND hParentWnd;
  99. TypTextBlock *headerBlock, *body, *attachment, *tail;
  100. unsigned int thisPart, numParts;
  101. int AttachmentState;
  102. char attachmentID[MAXINTERNALLINE], origSubject[MAXINTERNALLINE];    
  103. WndEdit *thisPostWnd;
  104. int thisDocType;
  105.  
  106. unsigned int editSize;        // cannot exceed 64k in an edit window
  107. unsigned int editMaxSize;
  108. char *editMem;
  109. int aMode;                // attachment mode for ended line chars
  110. int saveReviewAttach;
  111.  
  112. #define MAX_COMM_SPOOL 1000
  113. unsigned int commSpoolLen;
  114. char *commSpool;
  115.  
  116. /* ---------------------------------------------------------------------------
  117.  * EncodingTypeToNum converts a string describing a coding type into the
  118.  * internal numeric representation
  119.  */
  120. int
  121. EncodingTypeToNum (char *str)
  122. {
  123.     if (!stricmp (str, "Base-64"))
  124.         return CODE_BASE64;
  125.     else if (!stricmp (str, "UU"))
  126.         return CODE_UU;
  127.     else if (!stricmp (str, "XX"))
  128.         return CODE_XX;
  129.     else if (!stricmp (str, "Custom"))
  130.         return CODE_CUSTOM;
  131.     else if (!stricmp (str, "None"))
  132.         return CODE_NONE;
  133.     else
  134.         return CODE_UNKNOWN;
  135. }
  136.  
  137. /* ---------------------------------------------------------------------------
  138.  * hParentWnd is handle to multi-line edit posting window
  139.  *    Called by wvpost.c after file selected and attachment dialog completed
  140.  *    Based on globals set in attachment dialog, creates attachment
  141.  *    possibly encoded, with MIME headers, and posts it
  142.  *    
  143.  *    If ReviewAttach is set, add attachment to post/mail windows (creating
  144.  *    new windows as necessary for message/partial)
  145.  */
  146. void
  147. Attach (WndEdit *WndPost, char *fileName, int DocType)
  148. {
  149.     time_t theTime;
  150.     unsigned long offset, mimeUsage;
  151.             
  152.     hParentWnd = WndPost->hWnd;
  153.     hThisEditWnd = WndPost->hWndEdit;
  154.     WndPost->dirty = DT_DIRTY;
  155.     thisDocType = DocType;
  156.     thisPostWnd = WndPost;    
  157.     // Initialize text blocks
  158.     if ((headerBlock= InitTextBlock (hCodedBlockWnd)) == NULL)
  159.         return;
  160.     if ((body       = InitTextBlock (hCodedBlockWnd)) == NULL)
  161.         return;
  162.     if ((tail       = InitTextBlock (hCodedBlockWnd)) == NULL)
  163.         return;
  164.     if ((attachment = InitTextBlock (hCodedBlockWnd)) == NULL)
  165.         return;
  166.  
  167.     aMode = ADD_TO_EDIT;    // end all lines in \r\n
  168.  
  169.     if (!ReviewAttach)
  170.     {
  171.         if ((commSpool = (char *)GlobalAllocPtr (GMEM_MOVEABLE, MAX_COMM_SPOOL*sizeof (char))) == NULL)
  172.         {
  173.             MessageBox (hParentWnd, "Memory allocation failure", "Comm Spool Init Failed", MB_OK);
  174.             return;
  175.         }            
  176.         commSpool[0] = '\0';
  177.         commSpoolLen = 0;
  178.     }
  179.     
  180.     saveReviewAttach = ReviewAttach;
  181.     if (DocType == DOCTYPE_MAIL)
  182.         ReviewAttach = TRUE;            // must review when mailing
  183.         
  184.     CreateStatusArea (hParentWnd);        // init hCodedBlockWnd and currentCoded
  185.     strcpy (currentCoded->ident, fileName);    // status info
  186.  
  187.     if (EncodingTypeNum == CODE_NONE)
  188.     {        
  189.         if (ReadFileToTextBlock (hParentWnd, attachment, fileName, aMode) == FAIL)
  190.             {  FinishAttachment (ABORT); return;  }
  191.     }
  192.     else     if (Encode (attachment, fileName, aMode) == FAIL)
  193.         {  FinishAttachment (ABORT); return;  }
  194.  
  195.     CodingState = ATTACH_POSTING;
  196.     InvalidateRect (hCodedBlockWnd, NULL, TRUE);    // clear background
  197.  
  198.     time (&theTime);
  199.     sprintf (attachmentID, "\"%ld@%s\"", theTime, UserName);
  200.         
  201.     if (SplitCurrentHeader (headerBlock, body, tail, origSubject))
  202.         {  FinishAttachment (ABORT); return;  }
  203.     
  204.     if (!GenerateMIME || BlankBeforeMIME)
  205.        if (AddEndedLineToTextBlock (headerBlock, "", aMode))
  206.         {  FinishAttachment (ABORT); return;  }
  207.  
  208.         if (GenerateMIME && AddMIMEVersion (headerBlock))
  209.             {  FinishAttachment (ABORT); return;  }
  210.   
  211.     // if header+tail won't fit in ArticleSplitLength bytes, can't do it
  212.     if (headerBlock->numBytes + tail->numBytes + 200 > ArticleSplitLength)
  213.     {
  214.         MessageBox (hCodedBlockWnd, "Article split length too short to contain headers", "Article Split Length Too Small", MB_OK);
  215.         FinishAttachment (ABORT); return;
  216.     }
  217.  
  218.     // if the attachment won't fit in this article, force start in next
  219.     if (!AttachInNewArt && 
  220.        (body->numBytes + headerBlock->numBytes + tail->numBytes + 200 > ArticleSplitLength))
  221.     {
  222.         MessageBox (hCodedBlockWnd, "Beginning attachment in next article", "Edit Window Space Shortage", MB_OK);
  223.         AttachInNewArt == TRUE;
  224.     }
  225.     
  226.     if (ArticleSplitLength > 0)
  227.     {
  228.         offset = (AttachInNewArt) ? 0 : body->numBytes;
  229.         // calc encodelength/splitlength rounded up
  230.         numParts = (int)((attachment->numBytes + offset + 
  231.              ArticleSplitLength) / ArticleSplitLength);
  232.         // now add in the length of header+sig for each part
  233.         // the extra 200 bytes is rough avg for the MIME headers
  234.         // on each part.  Round up to next articlesplitlen
  235.         mimeUsage = GenerateMIME ? 200L : 0L;
  236.         numParts = (int)((attachment->numBytes + offset + 
  237.              (long)numParts*(headerBlock->numBytes + tail->numBytes + mimeUsage) + 
  238.              ArticleSplitLength) / ArticleSplitLength);
  239.         } else
  240.             numParts = 1;
  241.  
  242.     AttachmentState = ATTACH_START;
  243.     ProcessAttach (CONTINUE);
  244. }
  245.  
  246. /* ---------------------------------------------------------------------------
  247.  * ProcessAttach
  248.  * this is written as a state machine to allow interruption in !ReviewMode
  249.  * to wait for News Server comm responses.
  250.  * Thus in !ReviewMode, after an Initiate/EndAttach, we give up control
  251.  * and wvutil calls ProcessAttach when ready to continue
  252.  */
  253. void
  254. ProcessAttach (int action)
  255. {
  256.     static unsigned long attachLine, byteCount, saveHeaderLines, saveHeaderBytes, dummyLong;
  257.     static BOOL MultipartMixed;
  258.     char temp[MAXINTERNALLINE];
  259.     static int abort;
  260.     int i, found;
  261.         
  262.     if (action == ABORT) 
  263.     {
  264.         MessageBox (hCodedBlockWnd, "Posting with attachment aborted", "Attachment", MB_OK|MB_ICONHAND);
  265.         AttachmentState = ATTACH_DONE;
  266.     }
  267.  
  268.   while (AttachmentState != ATTACH_NONE) {
  269.     switch (AttachmentState) {
  270.  
  271.     case ATTACH_START:
  272.         abort = ABORT;
  273.         byteCount = 0;
  274.         MultipartMixed = FALSE;
  275.         attachLine = 0;
  276.         saveHeaderLines = headerBlock->numLines;
  277.         saveHeaderBytes = headerBlock->numBytes;
  278.         currentCoded->numLines = 0;
  279.         currentCoded->numBytes = 0;
  280.  
  281.         if (AttachInNewArt)
  282.         {
  283.             thisPart = 0;
  284.             AttachmentState = ATTACH_FIRST_IN_NEXT_WND;
  285.         }
  286.         else
  287.         {
  288.             thisPart = 1;
  289.             AttachmentState = ATTACH_FIRST_IN_THIS_WND;
  290.         }
  291.         currentCoded->sequence = thisPart;    // status window info
  292.  
  293.         if (InitiateAttach (CURRENT_WND, thisDocType))
  294.             {  AttachmentState = ATTACH_DONE; break;  }
  295.         if (!ReviewAttach)
  296.             return;
  297.  
  298.         break;
  299.  
  300.     case ATTACH_FIRST_IN_THIS_WND:
  301.         if (!ReviewAttach)
  302.             CommState = ST_POST_WAIT_END;
  303.             
  304.         if (body->numLines != 0)    // if have body, then mixed
  305.         {
  306.           GenerateSubject (headerBlock, origSubject, thisPart, numParts);
  307.           saveHeaderBytes = headerBlock->numBytes;
  308.  
  309.           if (GenerateMIME)
  310.           {
  311.             if (AddEndedLineToTextBlock (headerBlock, "Content-Type: multipart/mixed;",aMode))
  312.               {  AttachmentState = ATTACH_DONE; break;  }
  313.  
  314.             sprintf (temp, "     Boundary=\"%s\"", MIMEBoundary);
  315.             if (AddEndedLineToTextBlock (headerBlock, temp, aMode))
  316.             {  AttachmentState = ATTACH_DONE; break;  }
  317.           
  318.             // blank preamble here
  319.             // each boundary must be preceded by a CRLF (add "")
  320.             if (AddEndedLineToTextBlock (headerBlock, "", aMode))
  321.             {  AttachmentState = ATTACH_DONE; break;  }
  322.             sprintf (temp, "--%s", MIMEBoundary);
  323.             if (AddEndedLineToTextBlock (headerBlock, temp, aMode))
  324.             {  AttachmentState = ATTACH_DONE; break;  }
  325.                   
  326.                     // explicitly type the encapsulated section
  327.             if (AddEndedLineToTextBlock (headerBlock, "Content-Type: text/plain", aMode))
  328.             {  AttachmentState = ATTACH_DONE; break;  }
  329.             if (AddEndedLineToTextBlock (headerBlock, "", aMode))
  330.             {  AttachmentState = ATTACH_DONE; break;  }
  331.           
  332.             if (AddEndedLineToTextBlock (body, "", aMode))
  333.             {  AttachmentState = ATTACH_DONE; break;  }
  334.             sprintf (temp, "--%s", MIMEBoundary);
  335.             if (AddEndedLineToTextBlock (body, temp, aMode))
  336.             {  AttachmentState = ATTACH_DONE; break;  }
  337.           } 
  338.           else    // no MIME
  339.           {
  340.               if (AddEndedLineToTextBlock (body, "", aMode))
  341.             {  AttachmentState = ATTACH_DONE; break;  }
  342.               if (AddEndedLineToTextBlock (body, "BEGIN --- CUT HERE --- Cut Here --- cut here ---", aMode))
  343.             {  AttachmentState = ATTACH_DONE; break;  }
  344.           }
  345.           PostTextBlock (headerBlock);
  346.           PostTextBlock (body);
  347.           MultipartMixed = TRUE;
  348.           byteCount = headerBlock->numBytes + body->numBytes;
  349.         }
  350.         AttachmentState = ATTACH_MAIN;
  351.         break;
  352.  
  353.     case ATTACH_FIRST_IN_NEXT_WND:
  354.         // post current stuff, begin attachment in NEXT article
  355.         if (!ReviewAttach)
  356.             CommState = ST_POST_WAIT_END;
  357.  
  358.         GenerateSubject (headerBlock, origSubject, thisPart, numParts);
  359.         saveHeaderBytes = headerBlock->numBytes;
  360.         
  361.         if (GenerateMIME)
  362.         {
  363.                   if (AddEndedLineToTextBlock (headerBlock, "Content-Type: text/plain", aMode))
  364.             {  AttachmentState = ATTACH_DONE; break;  }
  365.         }
  366.         if (AddEndedLineToTextBlock (headerBlock, "", aMode))
  367.             {  AttachmentState = ATTACH_DONE; break;  }
  368.         PostTextBlock (headerBlock);
  369.         PostTextBlock (body);  
  370.         PostTextBlock (tail);  
  371.  
  372.         thisPart = 1;
  373.         currentCoded->sequence = thisPart;    // status window info
  374.         AttachmentState = ATTACH_WAIT;
  375.  
  376.         EndAttach ();
  377.         if (!ReviewAttach)
  378.             return;
  379.  
  380.         break;
  381.  
  382.     case ATTACH_WAIT:
  383.         if (InitiateAttach (NEW_WND, thisDocType))
  384.             {  AttachmentState = ATTACH_DONE; break;  }
  385.         AttachmentState = ATTACH_MAIN;
  386.         if (!ReviewAttach)
  387.             return;
  388.  
  389.         break;
  390.  
  391.     case ATTACH_MAIN:
  392.         if (!ReviewAttach)
  393.             CommState = ST_POST_WAIT_END;
  394.  
  395.         headerBlock->numLines = saveHeaderLines;    // reset to orig header
  396.         headerBlock->numBytes = saveHeaderBytes;
  397.  
  398.         GenerateSubject (headerBlock, origSubject, thisPart, numParts);
  399.         saveHeaderBytes = headerBlock->numBytes;
  400.                 if (GenerateMIME)
  401.                 {
  402.           if (numParts > 1)
  403.           {
  404.             if (AddEndedLineToTextBlock (headerBlock, "Content-Type: message/partial;", aMode))
  405.                 {  AttachmentState = ATTACH_DONE; break;  }
  406.             sprintf (temp, "     id=%s;", attachmentID);
  407.             if (AddEndedLineToTextBlock (headerBlock, temp, aMode))
  408.                 {  AttachmentState = ATTACH_DONE; break;  }
  409.             sprintf (temp, "     number=%d; total=%d", thisPart, numParts);
  410.             if (AddEndedLineToTextBlock (headerBlock, temp, aMode))
  411.                 {  AttachmentState = ATTACH_DONE; break;  }
  412.  
  413.             if (AddEndedLineToTextBlock (headerBlock, "", aMode))
  414.                 {  AttachmentState = ATTACH_DONE; break;  }
  415.           }
  416.           if (thisPart == 1)
  417.           {
  418.              for (i = 0, found = FALSE; i < NUM_CONTENT_TYPES && !found; i++)
  419.                 if (!_stricmp (ContentType, ContentTypes[i]))
  420.                     found = TRUE;
  421.  
  422.             if (!_stricmp (ContentType, "Other") || !found)
  423.                 sprintf (temp, "Content-Type: Application/octet-stream");
  424.             else                    
  425.                 sprintf (temp, "Content-Type: %s", ContentType);
  426.  
  427.             if (AddEndedLineToTextBlock (headerBlock, temp, aMode))
  428.                 {  AttachmentState = ATTACH_DONE; break;  }
  429.                      
  430.             switch (EncodingTypeNum)
  431.             {
  432.             case CODE_UU:    
  433.                 sprintf (temp, "Content-Transfer-Encoding: %s", MIMEUUType);
  434.                 break;
  435.             case CODE_XX:
  436.                 sprintf (temp, "Content-Transfer-Encoding: %s", MIMEXXType);
  437.                 break;
  438.             case CODE_CUSTOM:
  439.                 sprintf (temp, "Content-Transfer-Encoding: %s", MIMECustomType);
  440.                 break;
  441.             case CODE_BASE64:
  442.                 strcpy (temp, "Content-Transfer-Encoding: Base64");
  443.                 break;
  444.             case CODE_NONE:
  445.                 temp[0] = '\0';
  446.             }
  447.             if (temp[0] != '\0' &&
  448.                (AddEndedLineToTextBlock (headerBlock, temp, aMode)))
  449.                 {  AttachmentState = ATTACH_DONE; break;  }
  450.  
  451.             if (AddEndedLineToTextBlock (headerBlock, "",aMode))
  452.                 {  AttachmentState = ATTACH_DONE; break;  }
  453.           }
  454.         }
  455.  
  456.         // if multipart/mixed and first part, then the main header 
  457.         // was already displayed, so we now want to post from the end 
  458.         // of that header on (byteCount already set above)
  459.         if (MultipartMixed && thisPart == 1)
  460.         {
  461.             if (GenerateMIME)
  462.                 PostPartialTextBlock (headerBlock, saveHeaderLines, byteCount-saveHeaderBytes);
  463.         }
  464.         else                                
  465.         {
  466.             PostTextBlock (headerBlock);
  467.             byteCount = headerBlock->numBytes;
  468.         }
  469.  
  470.         if (numParts > 1)
  471.         {
  472.             if (attachLine == attachment->numLines)
  473.             {
  474.                 sprintf (temp, "[WinVn: Humble apologies: the # of attachment blocks was estimated incorrectly]\r");
  475.                 if (aMode == ADD_TO_EDIT) 
  476.                     strcat (temp, "\n");
  477.                 PostOneLine (temp, &dummyLong, 0);
  478.                 thisPart = numParts;
  479.             }
  480.             else
  481.             {
  482.                 // calculate number of bytes from attachment to use 
  483.                 // in this part: we've already used byteCount bytes
  484.                 // in headers, and we need to leave room for the tail
  485.                 byteCount = ArticleSplitLength - byteCount - tail->numBytes;
  486.                 if ((attachLine = PostPartialTextBlock (attachment, attachLine, byteCount)) == 0)
  487.                     {  AttachmentState = ATTACH_DONE; break;  }
  488.             }
  489.         }
  490.         else
  491.             PostTextBlock (attachment);
  492.         
  493.         // if this is the last section, don't end it since we may
  494.         // still have a boundary and a tail to add
  495.         if (thisPart + 1 <= numParts) 
  496.         {
  497.             thisPart++;
  498.             AttachmentState = ATTACH_WAIT;
  499.  
  500.             currentCoded->sequence = thisPart;    // status window info
  501.             currentCoded->numLines = 0;
  502.  
  503.             EndAttach ();
  504.             if (!ReviewAttach)
  505.                 return;
  506.         }
  507.         else // Done with attachment
  508.         {    
  509.             // if multi-part/mixed then need to attach the final boundary
  510.             if (GenerateMIME && MultipartMixed)
  511.             {
  512.                 ResetTextBlock (body);
  513.                 if (AddEndedLineToTextBlock (body, "", aMode))
  514.                     {  AttachmentState = ATTACH_DONE; break;  }
  515.                 sprintf (temp, "--%s--", MIMEBoundary);
  516.                 if (AddEndedLineToTextBlock (body, temp, aMode))
  517.                     {  AttachmentState = ATTACH_DONE; break;  }
  518.                 PostTextBlock (body);
  519.             }    
  520.                 
  521.             // Signature/tail ends up after last boundary, in epilogue
  522.             PostTextBlock (tail);
  523.             AttachmentState = ATTACH_DONE;
  524.             abort = CONTINUE;        // successful
  525.  
  526.             EndAttach ();
  527.             if (!ReviewAttach)
  528.                 return;
  529.         }
  530.         break;
  531.  
  532.     case ATTACH_DONE:
  533.         FinishAttachment (abort); 
  534.         AttachmentState = ATTACH_NONE;
  535.         break;
  536.     
  537.     } // end of switch (AttachmentState)
  538.   
  539.   } // end of while (AttachmentState != ATTACH_NONE)
  540.   return;
  541. }
  542.  
  543. void
  544. FinishAttachment (int action)
  545. {
  546.     char temp[255];
  547.     
  548.     FreeTextBlock (headerBlock);
  549.     FreeTextBlock (body);
  550.     FreeTextBlock (attachment);
  551.     
  552.     CodingState = INACTIVE;
  553.     DestroyStatusArea();
  554.  
  555.     // if we are managing the post (not reviewing), then we must close the
  556.     // one post window when we're through (don't close if aborted)
  557.     if (!ReviewAttach)
  558.     {
  559.         if (action == CONTINUE)
  560.         {
  561.             sprintf (temp, "Posting completed in %d part", numParts);
  562.             if (numParts > 1)
  563.                 strcat (temp, "s");
  564.                 
  565.             MessageBox (thisPostWnd->hWnd, temp, "Posting done", MB_OK|MB_ICONINFORMATION);
  566.             DestroyWindow (thisPostWnd->hWnd);
  567.         }
  568.         GlobalFreePtr (commSpool);
  569.     }        
  570.     ReviewAttach = saveReviewAttach;
  571. }
  572.  
  573. /* ------------------------------------------------------------------------
  574.  *     Reads current text from the posting edit buffer, 
  575.  *    splits all RFC822 header stuff into header block,
  576.  *        save off subject line from header during copy
  577.  *    and body up to any signature into the body block.
  578.  *    from signature on goes into tail block
  579.  *    This is so we can insert our MIME stuff right in between
  580.  *    Assumes there is a null (blank) line between RFC822 header and body
  581.  *    Tailing signature
  582.  */
  583. #define READING_HEADER     1
  584. #define READING_BODY    2
  585. #define READING_TAIL    3
  586. BOOL
  587. SplitCurrentHeader (TypTextBlock *header, TypTextBlock *body, TypTextBlock *tail, char *origSubject)
  588. {
  589.         char *editBuf, *ptr, *end;
  590.         int state;
  591.     char temp[MAXINTERNALLINE];
  592.             
  593.     origSubject[0] = '\0';
  594.     if ((editBuf = GetEditText (hThisEditWnd)) == NULL)
  595.         return (FAIL);
  596.     
  597.     for (state = READING_HEADER, ptr = editBuf; *ptr != '\0';)
  598.     {
  599.         end = strstr (ptr, "\r\n");     // all edit buf lines end in \r\n
  600.         if (end == NULL)
  601.         {
  602.             strcpy (temp, ptr);    // last line - no \r\n
  603.             *ptr = '\0';        // finish loop, then stop
  604.         }
  605.         else
  606.         {
  607.             *end = '\0';            // strip \r\n
  608.             strcpy (temp, ptr);
  609.             ptr = end + 2;          // skip \r\n for next line
  610.         }
  611.         switch (state)
  612.         {
  613.           case READING_HEADER:
  614.             if (!IsBlankStr(temp))
  615.             {
  616.                 if (AddEndedLineToTextBlock (header, temp, aMode))
  617.                     return (FAIL);;
  618.                 if (!_strnicmp (temp, "subject:", 8))
  619.                     strcpy (origSubject, temp);
  620.             }
  621.             else    // switch to reading body                    
  622.             {
  623.                 state = READING_BODY;
  624.                 continue;
  625.             }
  626.             break;
  627.         
  628.           case READING_BODY:
  629.             if (body->numLines == 0 && IsBlankStr (temp))
  630.                 continue;    // skip leading blank lines
  631.                 
  632.             if (EnableSig && Signature->numLines > 0 && 
  633.                 !strcmp (temp, TextBlockLine (Signature, 0)))
  634.             {
  635.                 if (AddEndedLineToTextBlock (tail, temp, aMode))
  636.                     return (FAIL);
  637.                 state = READING_TAIL;
  638.                 continue;
  639.             }
  640.             if (AddEndedLineToTextBlock (body, temp, aMode))
  641.                 return (FAIL);
  642.             break;
  643.         
  644.           case READING_TAIL:
  645.             if (AddEndedLineToTextBlock (tail, temp, aMode))
  646.                 return (FAIL);
  647.             }       
  648.  
  649.     }
  650.     GlobalFreePtr (editBuf);
  651.     return (SUCCESS);
  652. }    
  653.     
  654. /* ------------------------------------------------------------------------
  655.  *    Start/end posting
  656.  */
  657. BOOL
  658. InitiateAttach (int useWnd, int DocType)
  659. {
  660.     WndEdit *NewWndPost;
  661.         
  662.     if (ReviewAttach)
  663.     {
  664.         /* create new window for each posted section
  665.          * if attach-now or thisPart==0 use existing window for 1st section
  666.          */
  667.         if (useWnd == NEW_WND)
  668.         {
  669.             if ((hParentWnd = CreatePostingWnd (hParentWnd, NULL, DocType)) == NULL)
  670.             {
  671.                 MessageBox (hParentWnd, "Failed to create new attachment window", "Posting Window Creation", MB_OK);
  672.                 return (FAIL);
  673.             }
  674.                             
  675.             if (DocType == DOCTYPE_POSTING)
  676.                NewWndPost = getWndEdit(WndPosts, hParentWnd, MAXPOSTWNDS);
  677.             else
  678.                NewWndPost = getWndEdit(WndMails, hParentWnd, MAXMAILWNDS);
  679.             
  680.             NewWndPost->dirty = DT_DIRTY;
  681.             hThisEditWnd = NewWndPost->hWndEdit;
  682.         }
  683.         sprintf (str, "Review Attachment Part %d of %d", thisPart, numParts);
  684.         SetWindowText (hParentWnd, str);
  685.         if ((editMem = (char *) GlobalAllocPtr (GMEM_MOVEABLE|GMEM_ZEROINIT, EDIT_SIZE_INC*sizeof(char))) == NULL)
  686.         {
  687.             MessageBox (hParentWnd, "Memory allocation failure", "Attachment", MB_OK);
  688.             return (FAIL);
  689.         }
  690.         editMaxSize = EDIT_SIZE_INC;
  691.         editSize = 0;
  692.     }
  693.     else        
  694.         if (!StartPost (thisPostWnd))
  695.             return (FAIL);
  696.  
  697.     return (SUCCESS);
  698. }
  699.  
  700. void
  701. EndAttach (HWND hParentWnd)
  702. {
  703.     unsigned long dummyLong;
  704.     
  705.     if (ReviewAttach)
  706.     {
  707.         SetEditText (hThisEditWnd, editMem);
  708.         GlobalFreePtr (editMem);
  709.     }
  710.     else
  711.     {
  712.         PostOneLine (".\r\n", &dummyLong, 0);
  713.         FlushCommSpool ();
  714.     }
  715. }
  716.  
  717. void
  718. FlushCommSpool ()
  719. {
  720.     if (commSpoolLen > 0)
  721.     {
  722.         PutCommData (commSpool, commSpoolLen);
  723.         commSpoolLen = 0;
  724.         commSpool[0] = '\0';
  725.     }
  726. }
  727.  
  728. /* ------------------------------------------------------------------------
  729.  * Post the contents of a text block
  730.  *    Assumes posting already initiated, and does not end the posting
  731.  *    Lines must end in \r\n
  732.  */
  733.  
  734. int 
  735. PostOneLine (char *str, unsigned long *byteCount, unsigned long maxBytes)
  736. {
  737.     unsigned int len;
  738.  
  739.     len = strlen (str);
  740.         
  741.     *byteCount += (long)len;
  742.     if (maxBytes > 0 && *byteCount >= maxBytes)
  743.         return (SUCCESS);
  744.  
  745.     if (ReviewAttach)
  746.     {
  747.         editSize += len;
  748.         if (editSize >= editMaxSize)
  749.         {
  750.             editMaxSize += max(EDIT_SIZE_INC, len);
  751.             if ((editMem = GlobalReAllocPtr (editMem, editMaxSize*sizeof(char), GMEM_MOVEABLE)) == NULL)
  752.             {
  753.                 MessageBox (hThisEditWnd, "Memory allocation failure", "Build Attachment", MB_OK);
  754.                 return (FAIL);
  755.             }
  756.         }
  757.         strcat (editMem, str);
  758.         currentCoded->numBytes = (long)editSize;
  759.     }
  760.     else
  761.     {
  762.         if (commSpoolLen + len >= MAX_COMM_SPOOL)
  763.             FlushCommSpool ();
  764.  
  765.         strcat (commSpool, str);
  766.         commSpoolLen += len;
  767.         currentCoded->numBytes += len;
  768.     }
  769.  
  770.     return (SUCCESS);
  771. }
  772.  
  773. void
  774. PostTextBlock (TypTextBlock *block)
  775. {
  776.     PostPartialTextBlock (block, 0L, 0L);
  777. }
  778.  
  779. unsigned long
  780. PostPartialTextBlock (TypTextBlock *block, unsigned long start, unsigned long maxBytes)
  781. {
  782.     register unsigned long i;
  783.     unsigned long byteCount;
  784.  
  785.     byteCount = 0;    
  786.             
  787.     for (i = start; i < block->numLines; i++)
  788.     {
  789.         if (PostOneLine (TextBlockLine (block, i), &byteCount, maxBytes) == FAIL)
  790.             return (0);
  791.         
  792.         if (maxBytes > 0 && byteCount >= maxBytes)
  793.             break;
  794.             
  795.         currentCoded->numLines++;
  796.         if (currentCoded->numLines % STATUS_UPDATE_FREQ == 0)
  797.             UpdateBlockStatus();
  798.     }
  799.     UpdateBlockStatus();    // show final size
  800.     return (i);
  801. }
  802. /* ------------------------------------------------------------------------
  803.  *    Add a line to the given header block which contains the MIME-Version
  804.  */
  805. BOOL
  806. AddMIMEVersion (TypTextBlock *header)
  807. {
  808.     char temp[MAXINTERNALLINE];
  809.     
  810.     sprintf (temp, "MIME-Version: %s", MIME_VERSION);
  811.     if (AddEndedLineToTextBlock (header, temp, aMode))
  812.         return (FAIL);;
  813.  
  814.     return (SUCCESS);
  815. }
  816.  
  817. /* ------------------------------------------------------------------------
  818.  *    Generate a subject line based on orig subject line, and the
  819.  *     subject template
  820.  *        Replace %f with AttachFileName
  821.  *        Replace %p with part #
  822.  *        Replace %0p with part # 0 padded to length of total # parts
  823.  *        Replace %t with total # parts
  824.  *        Replace %s with orig subject content
  825.  *
  826.  */
  827. void
  828. GenerateSubject (TypTextBlock *header, char *origSubject, 
  829.          unsigned int part, unsigned int numParts)
  830. {
  831.     register char *src, *dest, *ptr;
  832.     register unsigned long num;
  833.     char newSubject[MAXINTERNALLINE];
  834.     char endLine[3], numStr[10];
  835.     int numZeros;
  836.     extern char *NameWithoutPath ();
  837.  
  838.     for (num = 0; num < header->numLines; num++)
  839.         if (!_strnicmp (TextBlockLine (header, num), "subject:", 8))
  840.             break;
  841.         
  842.     ptr = strpbrk (TextBlockLine (header, num), "\n\r");
  843.     strcpy (endLine, ptr);
  844.  
  845.     for (src = SubjectTemplate, dest = newSubject; *src != NULL;)
  846.     {
  847.         if (*src == '%')
  848.         {
  849.             src++;
  850.             switch (*src)
  851.             {
  852.             case '%':        // literal percent is %%
  853.                 *dest++ = '%';
  854.                 break;
  855.             case 's':
  856.                 for (ptr = origSubject; *ptr; *dest++ = *ptr++);
  857.                 break;
  858.             case 'f':
  859.                 NameWithoutPath (str, AttachFileName);                
  860.                 for (ptr = str; *ptr; *dest++ = *ptr++);
  861.                 break;
  862.  
  863.             case '0':
  864.                 if (*(src+1) == 'p')
  865.                 {
  866.                     src++;    // skip zero
  867.                     // zero-pad number to length of numParts string
  868.                     itoa (numParts, numStr, 10);    // longer or equal in length to part
  869.                     itoa (part, str, 10);
  870.                     
  871.                     for (numZeros = strlen(numStr) - strlen(str); numZeros; numZeros--)
  872.                         *dest++ = '0';
  873.                     
  874.                     for (ptr = str; *ptr; *dest++ = *ptr++);
  875.                 }
  876.                 break;
  877.                 
  878.             case 'p':
  879.                 itoa (part, numStr, 10);
  880.                 for (ptr = numStr; *ptr; *dest++ = *ptr++);
  881.                 break;
  882.  
  883.             case 't':
  884.                 itoa (numParts, numStr, 10);
  885.                 for (ptr = numStr; *ptr; *dest++ = *ptr++);
  886.                 break;
  887.  
  888.             default:
  889.                 *dest++ = '%';
  890.                 *dest++ = *src;
  891.                 break;
  892.  
  893.             }
  894.             src++;    // skip control char after %
  895.             continue;
  896.         }
  897.         *dest++ = *src++;    
  898.     }
  899.     *dest = '\0';   
  900.     strcat (dest, endLine);    // replace end of line
  901.  
  902.     ReplaceLineInTextBlock (header, num, newSubject);
  903. }
  904.  
  905.  
  906. #if OLD_PLAIN_FILE
  907. /* ------------------------------------------------------------------------
  908.  *     Directly modifies the Edit buffer of the parent multi-line edit wnd
  909.  *    Appends contents of fileName to buffer
  910.  */
  911. void
  912. AppendPlainFileToPost (char *fileName)
  913. {
  914.     HFILE hFile;
  915.     register unsigned int i;
  916.         char *editBuf, *inBuf;
  917.         unsigned int endPtr, size, numRead;
  918.         
  919.         inBuf = (char *) GlobalAllocPtr(GMEM_FIXED, 2048*sizeof(char));
  920.     editBuf = GetEditText (hThisEditWnd);
  921.     
  922.     size = strlen (editBuf);        
  923.     if ((hFile = _lopen (fileName, READ)) == HFILE_ERROR)
  924.     {
  925.         sprintf(str, "Could not open file %s for read", fileName);
  926.         MessageBox (hParentWnd, str, "Attachment Open File Error", MB_OK);
  927.         return;
  928.     }
  929.     
  930.         while (1)
  931.         {
  932.            if ((numRead = _lread(hFile, inBuf, 2048)) == 0)
  933.                 break;
  934.                for (i = 0; i < numRead; i++)
  935.           if (!__isascii (inBuf[i]))
  936.           {
  937.             sprintf(str, "File %s appears to contain non-ASCII data.\nBinary files must be encoded for attachment", fileName);
  938.             MessageBox (hParentWnd, str, "Non-ASCII File Error", MB_OK);
  939.             goto endAppend;
  940.           }
  941.         
  942.         endPtr = size;
  943.         size += numRead;
  944.         editBuf = GlobalReAllocPtr (editBuf, size, GMEM_MOVEABLE);
  945.         memmove (editBuf + endPtr, inBuf, numRead);
  946.     }
  947.    
  948.    endAppend:;
  949.     _lclose(hFile);
  950.  
  951.     SetEditText (hThisEditWnd, editBuf);
  952.         GlobalFreePtr (editBuf);
  953.         GlobalFreePtr (inBuf);
  954. }
  955. #endif                                                                       
  956.  
  957. #if OLD_HEADER
  958. /* ------------------------------------------------------------------------
  959.  * Returns true for lines which look like standard header lines
  960.  * Returns non-zero if should skip this line, else zero
  961.  * A binary search through a list of words would be really smart
  962.  * Lazy approach
  963.  */
  964. int
  965. TestRfc822HeaderLine (char *line)
  966. {
  967.     switch (tolower(line[0]))
  968.     {
  969.     case 'd': 
  970.         return (!_strnicmp(line, "date", 4));
  971.     case 'f': 
  972.         return (!_strnicmp(line, "follow", 6) ||
  973.               !_strnicmp(line, "from", 4));
  974.      case 'k':
  975.         return (!_strnicmp(line, "keywords", 8));
  976.         
  977.     case 'l':
  978.         return (!_strnicmp(line, "lines", 5));
  979.     case 'm':
  980.         return (!_strnicmp(line, "message", 7));
  981.     case 'n':
  982.         return (!_strnicmp(line, "newsgrou", 8) ||
  983.               !_strnicmp(line, "nntp", 4));
  984.     case 'o':
  985.         return (!_strnicmp(line, "organiza", 8) ||
  986.              !_strnicmp(line, "originat", 8));
  987.         case 'p':
  988.         return (!_strnicmp(line, "path", 4));
  989.     case 'r':
  990.         return (!_strnicmp(line, "referenc", 8) ||
  991.             !_strnicmp(line, "reply", 5));
  992.     case 's':
  993.          return (!_strnicmp(line, "sender", 6) ||
  994.              !_strnicmp(line, "subject:", 8) ||
  995.              !_strnicmp(line, "summary", 7));
  996.     case 'x':
  997.         return (!_strnicmp(line, "xref:", 5) ||
  998.                 !_strnicmp(line, "x-news", 6));
  999.     default:
  1000.         return (0);
  1001.     }
  1002. }    
  1003.  
  1004. #endif                                                                       
  1005.